Skip to content

Conversation

ArneZsng
Copy link
Contributor

@ArneZsng ArneZsng commented Oct 6, 2025

Using Gemini, we are frequently running into the MALFORMED_FUNCTION_CALL as the finish reason.
Google seemingly does not provide any context in the response for the reason of the MALFORMED_FUNCTION_CALL.

In these cases, the agent can continue retrying which I why I suggest to simply continue in the case of a MALFORMED_FUNCTION_CALL.

Closes #631

The issue is most prevalent when using Gemini 2.5 Flash (compared to Gemini 2.5 Pro where it also happens but less frequently).

Example chunk:

sdk_http_response=HttpResponse(
  headers=<dict len=11>
) candidates=[Candidate(
  finish_reason=<FinishReason.MALFORMED_FUNCTION_CALL: 'MALFORMED_FUNCTION_CALL'>,
  index=0
)] create_time=None model_version=None prompt_feedback=None response_id=None usage_metadata=GenerateContentResponseUsageMetadata(
  prompt_token_count=2595,
  prompt_tokens_details=[
    ModalityTokenCount(
      modality=<MediaModality.TEXT: 'TEXT'>,
      token_count=2595
    ),
  ],
  total_token_count=2595
) automatic_function_calling_history=None parsed=None

if candidate.finish_reason == 'STOP': # pragma: no cover
# Normal completion - skip this chunk
continue
elif candidate.finish_reason == 'MALFORMED_FUNCTION_CALL': # pragma: no cover
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test for this, based on a real-world response with this finish reason?

Is skipping the chunk the right thing to do? Would any more chunks follow or would this be the last one in the response? Is it valid to just move on and ignore the error?

If the response before this failure is empty, we retry the request, which I suppose is the goal:

# Go back to the model request node with an empty request, which means we'll essentially
# resubmit the most recent request that resulted in an empty response,
# as the empty response and request will not create any items in the API payload,
# in the hope the model will return a non-empty response this time.
ctx.state.increment_retries(ctx.deps.max_result_retries)
self._next_node = ModelRequestNode[DepsT, NodeRunEndT](_messages.ModelRequest(parts=[]))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MALFORMED_FUNCTION finishReason in Gemini candidate
2 participants